import __builtin__
import networking.ganet as ganet
from server.sessionstuff.player import Player
import networking.ganet.network.message as message


class NetworkManager(object):
    ###############################################################################################################
    ###############################################################################################################
    ########################                         C O M M O N                           ########################
    ###############################################################################################################
    ###############################################################################################################

    def __init__(self):
        self.init_network(54321)
        self.connected_players = []

    def init_network(self, port):
        self.nw = ganet.network.Network()
        self.my_listener = NetListener(self)
        self.nw.add_listener(self.my_listener)
        self.nw.startup(port)

    def add_player(self, player):
        self.connected_players.append(player)

    def remove_player(self, peer):
        if self.get_player_byid(peer.addr[1]) is not None:
            self.broadcast_chat_msg(str(self.get_player_byid(peer.addr[1]).name) + " disconnected.")

            self.connected_players.remove(self.get_player_byid(peer.addr[1]))

            for s in __builtin__.base.sessionmanager.sessions:
                if (s.attacker != 0 and str(s.attacker.peer.addr[1]) == str(peer.addr[1])) or (s.defender != 0 and str(s.defender.peer.addr[1]) == str(peer.addr[1])):
                    self.client_left_session(peer, s)

    def get_player_byid(self, uid):
        for u in self.connected_players:
            if str(u.peer.addr[1]) == str(uid):
                return u

    def get_player_bynick(self, nick):
        for u in self.connected_players:
            if str(u.name) == nick:
                return u

    def broadcast_msg(self, msg):
        for u in self.connected_players:
            u.peer.send_reliable_sequenced(msg)

    def prep_msg(self, msg_type):
        encoder = message.MessageEncoder()
        encoder.reset()
        encoder.write_string(msg_type)
        return encoder

    def update(self, dt):
        self.nw.update(dt)

    def exit(self):
        self.nw.shutdown()
        self.nw.remove_listener(self.my_listener)







    ###############################################################################################################
    ###############################################################################################################
    ########################                          L O B B Y                            ########################
    ###############################################################################################################
    ###############################################################################################################

    def send_client_enter_lobby(self, peer):
        encoder = self.prep_msg("enter_lobby")
        encoder.write_string(peer.addr[1])
        peer.send_reliable_sequenced(encoder.get_message())

        for u in self.connected_players:
            self.send_user_online(u.peer)

    def send_client_left(self, peer):
        encoder = self.prep_msg("client_left")
        encoder.write_string(peer.addr[1])
        msg = encoder.get_message()

        for u in self.connected_players:
            u.peer.send_reliable_sequenced(msg)

    def changed_nick(self, peer, dec):
        pl_id = dec.read_string()
        name = dec.read_string()

        pl = self.get_player_byid(pl_id)

        if pl is not None:
            if str(pl.name) == str(peer.addr[1]):
                self.broadcast_chat_msg(str(name) + " connected.")
            else:
                self.broadcast_chat_msg(str(pl.name) + " renamed to " + str(name))
            pl.name = name

    def got_chat_msg(self, peer, dec):
        self.broadcast_chat_msg(str(self.get_player_byid(peer.addr[1]).name) + " > " + dec.read_string())

    def broadcast_chat_msg(self, msg):
        encoder = self.prep_msg("chat_msg")
        encoder.write_string(msg)
        message = encoder.get_message()

        for u in self.connected_players:
            u.peer.send_reliable_sequenced(message)

    def send_changed_nick(self, old, new):
        encoder = self.prep_msg("changed_nick")
        encoder.write_string(old)
        encoder.write_string(new)
        msg = encoder.get_message()

        for u in self.connected_players:
            if u.name != new:
                u.peer.send_reliable_sequenced(msg)

    def send_user_online(self, peer):
        encoder = self.prep_msg("user_online")
        encoder.write_int(len(self.connected_players))
        peer.send_reliable_sequenced(encoder.get_message())

    def request_gamelist(self, peer):
        encoder = self.prep_msg("requested_gamelist")

        available = 0
        for s in __builtin__.base.sessionmanager.sessions:
            if s.is_role_free():
                available += 1
        print "available " + str(available)
        encoder.write_int(available)

        for s in __builtin__.base.sessionmanager.sessions:
            if s.is_role_free():
                encoder.write_int(s.ID)
                encoder.write_string(s.creator.name)
                print "added " + str(s.ID)

        peer.send_reliable_sequenced(encoder.get_message())

    def broadcast_gamelist(self):
        for c in self.connected_players:
            self.request_gamelist(c.peer)






    ###############################################################################################################
    ###############################################################################################################
    ########################                     C R E A T E   G A M E                     ########################
    ###############################################################################################################
    ###############################################################################################################

    def create_session(self, peer):
        encoder = self.prep_msg("session_created")
        ses = __builtin__.base.sessionmanager.create_session(self.get_player_byid(peer.addr[1]))
        encoder.write_int(ses.ID)
        encoder.write_int(peer.addr[1])
        encoder.write_string(ses.creator.name)
        peer.send_reliable_sequenced(encoder.get_message())

        self.broadcast_gamelist()

    def shutdown_session(self, dec):
        ses = dec.read_int()
        __builtin__.base.sessionmanager.remove_session(ses)

    def client_joins_session(self, peer, dec):
        ses_id = dec.read_int()
        ses = __builtin__.base.sessionmanager.get_session_byid(ses_id)
        pl = self.get_player_byid(peer.addr[1])

        if ses is not None:
            ses.pl_joins_session(pl)

            encoder = self.prep_msg("session_joined")
            encoder.write_int(ses.ID)
            peer.send_reliable_sequenced(encoder.get_message())

            self.send_update_roles(ses)

        self.broadcast_gamelist()

    def client_left_session(self, peer, ses):
        ses_id = ses.ID

        if ses.started or ses.creator.peer.addr[1] == peer.addr[1]:
            if ses.attacker != 0:
                self.send_session_closed(ses.attacker.peer)
            if ses.defender != 0:
                self.send_session_closed(ses.defender.peer)
            __builtin__.base.sessionmanager.remove_session(ses_id)
        elif ses.attacker.peer.addr[1] == peer.addr[1]:
            self.send_session_closed(ses.attacker.peer)
            ses.attacker = 0
            self.send_update_roles(ses)
        elif ses.defender.peer.addr[1] == peer.addr[1]:
            self.send_session_closed(ses.defender.peer)
            ses.defender = 0
            self.send_update_roles(ses)

        self.broadcast_gamelist()

    def send_session_closed(self, peer):
        encoder = self.prep_msg("session_closed")
        peer.send_reliable_sequenced(encoder.get_message())

    def toggle_roles(self, peer, dec):
        ses_id = dec.read_int()
        ses = __builtin__.base.sessionmanager.get_session_byid(ses_id)
        buf = ses.attacker
        ses.attacker = ses.defender
        ses.defender = buf

        self.send_update_roles(ses)

    def send_update_roles(self, ses):
        encoder = self.prep_msg("update_session_roles")
        if ses.attacker != 0:
            encoder.write_int(ses.attacker.peer.addr[1])
            encoder.write_string(ses.attacker.name)
        else:
            encoder.write_int(0)
            encoder.write_string("0")

        if ses.defender != 0:
            encoder.write_int(ses.defender.peer.addr[1])
            encoder.write_string(ses.defender.name)
        else:
            encoder.write_int(0)
            encoder.write_string("0")

        if ses.attacker != 0:
            ses.attacker.peer.send_reliable_sequenced(encoder.get_message())
        if ses.defender != 0:
            ses.defender.peer.send_reliable_sequenced(encoder.get_message())

    def start_session(self, peer, dec):
        ses_id = dec.read_int()
        ses = __builtin__.base.sessionmanager.get_session_byid(ses_id)

        encoderAtk = self.prep_msg("session_started")
        encoderAtk.write_int(0)
        encoderDef = self.prep_msg("session_started")
        encoderDef.write_int(1)
        #peer.send_reliable_sequenced(encoder.get_message())
        ses.attacker.peer.send_reliable_sequenced(encoderAtk.get_message())
        ses.defender.peer.send_reliable_sequenced(encoderDef.get_message())

        ses.start_session()





    ###############################################################################################################
    ###############################################################################################################
    ########################               R U N N I N G   S E S S I O N                   ########################
    ###############################################################################################################
    ###############################################################################################################

    def ses_place_tower(self, dec):
        __builtin__.base.sessionmanager.get_session_byid(dec.read_int()).place_tower(dec)

    def ses_place_unit(self, dec):
        __builtin__.base.sessionmanager.get_session_byid(dec.read_int()).place_unit(dec)

    def ses_place_warpzone(self, dec):
        __builtin__.base.sessionmanager.get_session_byid(dec.read_int()).place_warpzone(dec)

    def ses_upgrade(self, dec):
        __builtin__.base.sessionmanager.get_session_byid(dec.read_int()).upgrade(dec)

    def ses_select(self, dec):
        __builtin__.base.sessionmanager.get_session_byid(dec.read_int()).select(dec)




class NetListener(ganet.network.NetworkListener):
    def __init__(self, server):
        self.server = server

    def on_new_connection(self, peer):
        print 'LISTENER: new connection from %s:%d' % peer.addr
        self.server.add_player(Player(peer))
        self.server.send_client_enter_lobby(peer)

    def on_peer_lost(self, peer):
        print 'LISTENER: peer lost %s:%d' % peer.addr
        self.server.remove_player(peer)
        self.server.send_client_left(peer)

    def on_peer_disconnected(self, peer):
        print 'LISTENER: peer disconnected %s:%d' % peer.addr
        self.server.remove_player(peer)
        self.server.send_client_left(peer)

    def on_connection_accepted(self, peer):
        print 'LISTENER: peer accepted connection %s:%d' % peer.addr

    def on_connection_refused(self, peer):
        print 'LISTENER: peer refused %s:%d' % peer.addr

    def on_message(self, peer, msg):
        print 'LISTENER: message from %s:%d: %s' % (peer.addr[0], peer.addr[1], msg)
        dec = message.MessageDecoder()
        dec.reset(msg)
        type = dec.read_string()

        if type == "changed_nick":
            self.server.changed_nick(peer, dec)
        elif type == "request_gamelist":
            self.server.request_gamelist(peer)
        elif type == "number_user_online":
            self.server.send_user_online(peer)
        elif type == "chat_msg":
            self.server.got_chat_msg(peer, dec)
        elif type == "request_new_session":
            self.server.create_session(peer)
        elif type == "shutdown_session":
            self.server.shutdown_session(dec)
        elif type == "session_left":
            self.server.client_left_session(peer, __builtin__.base.sessionmanager.get_session_byid(dec.read_int()))
        elif type == "request_join_session":
            self.server.client_joins_session(peer, dec)
        elif type == "request_toggle_roles":
            self.server.toggle_roles(peer, dec)
        elif type == "request_session_start":
            self.server.start_session(peer, dec)
        elif type == "place_unit":
            self.server.ses_place_unit(dec)
        elif type == "place_tower":
            self.server.ses_place_tower(dec)
        elif type == "place_warpzone":
            self.server.ses_place_warpzone(dec)
        elif type == "upgrade":
            self.server.ses_upgrade(dec)
        elif type == "select":
            self.server.ses_select(dec)

    def on_error(self, error_num):
        print 'LISTENER: error: %d' % error_num